package com.tms.warehouse;

import java.util.ArrayList;
import java.util.List;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.tms.crud.StringTools;
import com.tms.warehouse.entity.Color;
import com.tms.warehouse.entity.Profile;
import com.tms.warehouse.entity.ProfileType;
import com.tms.warehouse.entity.Rack;
import com.tms.warehouse.entity.Series;
import com.tms.warehouse.entity.Shelf;
import com.tms.warehouse.mappings.AbstractMapper;
import com.tms.warehouse.mappings.Mapper;
import com.tms.warehouse.mappings.ProfileMapper;
import com.tms.warehouse.mappings.ProfileTypeMapper;
import com.tms.warehouse.mappings.RackMapper;
import com.tms.warehouse.mappings.ShelfMapper;

public class Warehouse {

	private final static Mapper<Color> COLOR_MAPPER = new AbstractMapper<Color>() {
	};
	private final static Mapper<Rack> RACK_MAPPER = new RackMapper();
	private final static Mapper<Shelf> SHELF_MAPPER = new ShelfMapper();
	private final static Mapper<ProfileType> PROFILE_TYPE_MAPPER = new ProfileTypeMapper();
	private final static Mapper<Profile> PROFILE_MAPPER = new ProfileMapper();
	private final static Mapper<Series> SERIES_MAPPER = new AbstractMapper<Series>() {
	};

	private static final DatastoreService datastore = DatastoreServiceFactory
			.getDatastoreService();

	// //////////////////////////////////////////////////////////////////////
	// RACK
	// //////////////////////////////////////////////////////////////////////

	public static List<Rack> getAllRacks() {

		return findAll(RACK_MAPPER);
	}

	public static void persistRack(String entityId, String name, int number,
			String seriesId) {

		Rack rack;
		if (StringTools.isEmpty(entityId)) {
			rack = new Rack();
		} else {
			rack = findRackById(entityId);
		}
		Series series = findSeriesById(seriesId);

		rack.setName(name);
		rack.setNumber(number);
		rack.setSeries(series);

		persist(rack, RACK_MAPPER);
	}

	public static Rack findRackById(String entityId) {

		return findById(entityId, RACK_MAPPER);
	}

	public static void deleteRack(String entityId) {

		delete(entityId, RACK_MAPPER);
	}

	// //////////////////////////////////////////////////////////////////////
	// COLOR
	// //////////////////////////////////////////////////////////////////////

	public static void persistColor(String entityId, String name) {

		Color color;
		if (StringTools.isEmpty(entityId)) {
			color = new Color();
		} else {
			color = findColorById(entityId);
		}
		color.setName(name);
		persist(color, COLOR_MAPPER);
	}

	public static List<Color> getAllColors() {

		return findAll(COLOR_MAPPER);
	}

	public static void createColor(String name) {

		persist(new Color(-1, name), COLOR_MAPPER);
	}

	public static Color findColorById(String entityId) {

		return findById(entityId, COLOR_MAPPER);
	}

	public static void deleteColor(String entityId) {

		delete(entityId, COLOR_MAPPER);
	}

	// //////////////////////////////////////////////////////////////////////
	// SERIES
	// //////////////////////////////////////////////////////////////////////

	public static List<Series> getAllSeries() {

		return findAll(SERIES_MAPPER);
	}

	public static void persistSeries(String entityId, String name) {

		Series series;
		if (StringTools.isEmpty(entityId)) {
			series = new Series();
		} else {
			series = findSeriesById(entityId);
		}
		series.setName(name);
		persist(series, SERIES_MAPPER);
	}

	public static Series findSeriesById(String entityId) {

		return findById(entityId, SERIES_MAPPER);
	}

	public static void deleteSeries(String entityId) {

		delete(entityId, SERIES_MAPPER);
	}

	// //////////////////////////////////////////////////////////////////////
	// SHELF
	// //////////////////////////////////////////////////////////////////////

	public static List<Shelf> getAllShelves() {

		return findAll(SHELF_MAPPER);
	}

	public static void persistShelf(String entityId, String name, int number,
			String rackId) {

		Shelf shelf;
		if (StringTools.isEmpty(entityId)) {
			shelf = new Shelf();
		} else {
			shelf = findShelfById(entityId);
		}
		
		Rack rack = findRackById(rackId);

		shelf.setName(name);
		shelf.setNumber(number);
		shelf.setRack(rack);

		persist(shelf, SHELF_MAPPER);
	}

	public static Shelf findShelfById(String entityId) {

		return findById(entityId, SHELF_MAPPER);
	}

	public static void deleteShelf(String entityId) {

		delete(entityId, SHELF_MAPPER);
	}

	// //////////////////////////////////////////////////////////////////////
	// PROFILE
	// //////////////////////////////////////////////////////////////////////

	public static List<Profile> getAllProfiles() {

		return findAll(PROFILE_MAPPER);
	}

	public static void persistProfile(String entityId, int length, int count,
			String notice, String profileTypeId, String colorId, String shelfId) {

		Profile profile;
		if (StringTools.isEmpty(entityId)) {
			profile = new Profile();
		} else {
			profile = findProfileById(entityId);
		}
		ProfileType type = findProfileTypeById(profileTypeId);
		Color color = findColorById(colorId);
		Shelf shelf = findShelfById(shelfId);

		profile.setLength(length);
		profile.setCount(count);
		profile.setNotice(notice);
		profile.setProfileType(type);
		profile.setColor(color);
		profile.setShelf(shelf);

		persist(profile, PROFILE_MAPPER);
	}

	public static Profile findProfileById(String entityId) {

		return findById(entityId, PROFILE_MAPPER);
	}

	public static void deleteProfile(String entityId) {

		delete(entityId, PROFILE_MAPPER);
	}

	// //////////////////////////////////////////////////////////////////////
	// PROFILETYPE
	// //////////////////////////////////////////////////////////////////////

	public static List<ProfileType> getAllProfileTypes() {

		return findAll(PROFILE_TYPE_MAPPER);
	}

	public static void persistProfileType(String entityId, String name,
			String articleNumber, String seriesId, String shelfId) {

		ProfileType profileType;
		if (StringTools.isEmpty(entityId)) {
			profileType = new ProfileType();
		} else {
			profileType = findProfileTypeById(entityId);
		}

		Shelf shelf = findShelfById(shelfId);
		Series series = findSeriesById(seriesId);

		profileType.setArticleNumber(articleNumber);
		profileType.setName(name);
		profileType.setSeries(series);
		profileType.setShelf(shelf);
		persist(profileType, PROFILE_TYPE_MAPPER);
	}

	public static ProfileType findProfileTypeById(String entityId) {

		return findById(entityId, PROFILE_TYPE_MAPPER);
	}

	public static void deleteProfileType(String entityId) {

		delete(entityId, PROFILE_TYPE_MAPPER);
	}

	// //////////////////////////////////////////////////////////////////////
	// COMMON
	// //////////////////////////////////////////////////////////////////////

	private static <T> void delete(String entityId, Mapper<T> mapper) {

		datastore.delete(KeyFactory.createKey(mapper.getType(),
				Long.parseLong(entityId)));
	}

	private static <T> T findById(String entityId, Mapper<T> mapper) {

		try {
			Key key = KeyFactory.createKey(mapper.getType(),
					Long.parseLong(entityId));
			return mapper.wrap(datastore.get(key));
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private static <T> void persist(T entity, Mapper<T> mapper) {

		datastore.put(mapper.unwrap(entity));
	}

	private static <T> List<T> findAll(Mapper<T> mapper) {

		return mapAll(findAllEntities(mapper.getType()), mapper);
	}

	private static Iterable<Entity> findAllEntities(String entityName) {

		// The Query interface assembles a query
		Query q = new Query(entityName);
		// PreparedQuery contains the methods for fetching query results
		// from the datastore
		PreparedQuery pq = datastore.prepare(q);
		return pq.asIterable();
	}

	private static <T> List<T> mapAll(Iterable<Entity> iter, Mapper<T> mapper) {

		List<T> result = new ArrayList<T>();
		for (Entity entity : findAllEntities(mapper.getType())) {
			result.add(mapper.wrap(entity));
		}
		return result;
	}
}
